'''
Created on Jul 21, 2011

@author: Krystian Brachmanski
'''

import logging
import subprocess

class GnuplotGenerator:
    '''
    Gnuplot generator base class
    
    @author: Krystian Brachmanski
    
    @requires: subprocess, logging
    '''

    log = logging.getLogger('yapcg.perfparser.gnuplotGenerator')
    
    main_header = "set term postscript eps enhanced color \"Helvetica\" 10\n"  
    gnuplot_template = "set palette negative \n\
set size 1,1 \n\
set style data fsteps \n\
unset border \n\
set autoscale \n\
set key below spacing 2 title 'Legend' box\n\
unset log \n\
unset label \n\
set xtic auto \n\
set ytic auto \n\
set grid \n\
set xlabel \"Time\" \n\
set ylabel \"Percentage [%]\" \n\
set style line 1 lt 1 lc rgb \"royalblue\" lw 2 \n\
set style line 2 lt 1 lc rgb \"orange-red\" lw 2 \n\
set style line 3 lt 1 lc rgb \"forest-green\" lw 2 \n\
set style line 4 lt 1 lc rgb \"red\" lw 2 \n\
set style line 5 lt 1 lc rgb \"dark-orange\" lw 2 \n\
set style line 6 lt 1 lc rgb \"royalblue\" lw 2 \n\
set style line 7 lt 1 lc rgb \"magenta\" lw 2 \n\
set style line 8 lt 1 lc rgb \"dark-red\" lw 2 \n\
set style line 9 lt 1 lc rgb \"black\" lw 2 \n\
set style line 10 lt 1 lc rgb \"#aaaaaa\" lw 2 \n\
\n\
set timefmt \"%H:%M:%S\" \n\
set xdata time \n\
set format x \"%H:%M:%S\" # optional\n" 
    
    def __init__(self):
        '''
        Constructor
        '''
        self.ylabel = "Percentage [%]"    
    
    def executeGnuPlot(self, gpFile):        
        '''
        Execute gnuplot as a new process, log the output and wait until it finishes
        '''
        OUT = open('gnuplot.out', 'a')
        self.log.debug("Generating chart for : " + gpFile + " log: " + 'gnuplot.out')
        
        try:
            subprocess.check_call(['gnuplot',gpFile],shell=False,stdout=OUT, stderr=OUT)
        except subprocess.CalledProcessError:
            self.log.error("gnuplot returned with non zero exit code check log files for errors: " + OUT.name)
            return None
        return 0
    
class SarGnuplotGenerator(GnuplotGenerator):
    '''
    Basic template for sar gnuplot's
    
    @requires: 
    '''
    
    main_title = "set title \"System CPU utilization\" \n"

    def __init__(self,column_map, keys = [ "total" ] ,format = "eps"):
        '''
        Constructor
        '''
        GnuplotGenerator.__init__(self)
        self.col_map = column_map
        self.format = format
        self.keys = keys

    def getColumn(self,key):
        '''
        Return column mapping
        
        @param key: column name
        '''
        return str(self.col_map[key])

    def writeDataSeries(self,fh,inFile):
        '''
        Helper method writes to the file series data information
        
        @param fh: file handle of the gnuplot configuration file
        @param inFile: name of data file from which the series are to be taken 
        
        '''
        
        fh.write("plot ")
        i = 0
        for key in self.keys:   
            if i > 0:
                fh.write(", \\\n");
            column = self.getColumn(key)
            fh.write("\"" + inFile + "\" using 1:" + column + " title \"" + key + "\" with lines ls " + str((i%20) + 1))
            i += 1

    def generateGnuplotFile(self, gpFile, inFile, outFile):
        '''
        Generate the gnuplot configuration file
        
        @param gpFile: name of the gnuplot file
        @param inFile: name of the data file that stoes the data to be visualized
        @param outFile: name of the file storing the chart, result of the gnuplot
        '''
        fh = open(gpFile, "w")
        
        fh.write(self.main_header)
        fh.write(self.main_title)
        fh.write(self.gnuplot_template)
        fh.write("set ylabel\"" + self.ylabel +"\" \n")
        
        if not outFile.endswith(self.format):
            outFile += "." + self.format
            
        if self.format == "pdf":
            fh.write("set output \"| ps2pdf - " + outFile + "\"\n\n");
        else:
            fh.write("set output \"" + outFile + "\"\n\n");
        
        self.writeDataSeries(fh,inFile)              
 
        fh.flush()
        fh.close() 
        self.executeGnuPlot(gpFile)        
        
class SarCpuGnuplotGenerator(SarGnuplotGenerator): 
    '''
    Generator for the sar cpu gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''
    def __init__(self,column_map, keys = [ "total" ] ,format = "eps"):
        '''
        Constructor
        
        @param column_map: dictionary that stores mapping between keys and column number
        @param keys: the name of the series to be presented (defualt: [\"total\"]
        @param format: the format of the result chart 
        '''
        SarGnuplotGenerator.__init__(self, column_map, keys, format)
        self.main_title = "set title \"System CPU utilization\" \n"

    def getColumn(self,key):
        '''
        @see: overrides the base class'es function for description please check it
        '''        
        if key == "total":
            return "(100 - $" + str(self.col_map[key]) + ")"
        else:
            return str(self.col_map[key])
        
class SarMemGnuplotGenerator(SarGnuplotGenerator):
    '''
    Generator for the sar mem gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''    
    def __init__(self, column_map, keys=["memused [\%]"], format = "eps"):
        '''
        Constructor
        
        @param column_map: dictionary that stores mapping between keys and column number
        @param keys: the name of the series to be presented (defualt: [\"memused [\%]\"]
        @param format: the format of the result chart 
        '''
                
        SarGnuplotGenerator.__init__(self, column_map, keys, format)
        self.main_title = "set title \"System memory utilization\" \n"
        
class pidstatGnuplotGenerator(GnuplotGenerator):
    '''
    Base generator for the pidstat cpu gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''
    format = "pdf"  
    showNoise = True
    
    sum_header = "set term postscript enhanced eps color \"Helvetica\" 10\n"
    det_header = "set term postscript eps enhanced color \"Helvetica\" 10\n"
      
    def __init__(self, showNoise = True, format="eps"):
        GnuplotGenerator.__init__(self)
        self.format = format
        self.showNoise = showNoise
        self.log.debug("Noise = " + str(self.showNoise))
        self.gnuplot_template += "set timefmt \"%s\" \n"
    
    def getIndexInTheList(self, id,tuple_list):
        '''
        @param id: the id value
        @param tuple_list: the list of tuples  
        @return: position in the list by id where tuple is (value, id)
        '''
        if tuple_list == None:
            return -1
        try:
            i = [y[1] for y in tuple_list].index(id)
            return i
        except ValueError:
            return -1

    def getDescriptionForName(self, name, column=None):
        '''
        @param name: the name of the series        
        @return: description of the series identified by name
        '''
        return name.replace("_"," ")
    
    def writeSeries(self, fh, columns, list, filemap):
        '''
        Helper method for writing series data into the output file
        @param fh: gnuplot config file hande
        @param columns: the list of columns to be written
        @param list: the of top processes 
        @param filemap: list of filenames       
        '''
        i = 0
        line = 0
        fh.write("plot ");
        for name in filemap:   
            if self.getIndexInTheList(name,list)<0:
                if self.showNoise == True:
                    for column in columns:
                        if i > 0:
                            fh.write(", \\\n");
                        fh.write("\"" + self.dirHandler.getDataFileName(name) + "\" using 1:" + column + " notitle with lines lw 1")
                        i += 1
            else:
                for column in columns:
                    if i > 0 and (line > 0 or self.showNoise):
                        fh.write(", \\\n");
                        fh.write("\"" + self.dirHandler.getDataFileName(name) + "\" using 1:" + column + " title \"" + self.getDescriptionForName(name,column) + "\" with lines ls " + str((line%20) + 1))
                        i += 1
                    line += 1
    
    def writeMainContent(self,fh,header):
        '''
        Helper function for writing the common begging of the gnuplot configuration file
        @param fh: gnuplot configuration file handle
        @param header: header  
        '''
        fh.write(header)
        fh.write(self.gnuplot_template)
    
class pidGlobalGenerator(pidstatGnuplotGenerator):
    '''
    Generator for the pidstat global statistics (all processes) gnuplot charts. Can be used for: mem, cpu
    
    @author: Krystian Brachmanski    
    @requires: 
    '''    
    def __init__(self,title, showNoise, format, dirHandler):
        pidstatGnuplotGenerator.__init__(self, showNoise, format)
        self.title = title
        self.dirHandler = dirHandler
        
    def writeGnuPlotFile(self, heap, filemap, series, outFile, gpFile):
        '''
        Generate the gnuplot configuration file and execute gnuplot for that file
        
        @param heap: the list of top values for which the description in the legend shall be displayed
        @param filemap: the list of file names to be used as input files
        @param series: the list of columns to be displayed on chart        
        @param outFile: name of the file storing the chart, result of the gnuplot
        @param gpFile: name of the gnuplot file
        '''
        fh = open(gpFile, "w")
        
        self.writeMainContent(fh,self.sum_header)
        fh.write("set title \""+ self.title + "\" \n");
        if self.format == "pdf":
            fh.write("set output \"| ps2pdf - " + outFile + "\"\n");
        else:
            fh.write("set output \"" + outFile + "\"\n");
        fh.write("\n")    
        self.writeSeries(fh, series, heap, filemap)
        fh.flush()
        fh.close() 
        self.executeGnuPlot(gpFile)
    
class pidPerProcessCpuGenerator(pidstatGnuplotGenerator):
    '''
    Generator for the pidstat per process cpu gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''
    def __init__(self, format="eps"):
        pidstatGnuplotGenerator.__init__(self, False, format)

    def writeGnuPlotFile(self, name, outFile, gpFile, inFile):
        '''
        Generate the gnuplot configuration file and execute gnuplot for that file
        
        @param name: the name of series
        @param outFile: name of the file storing the chart, result of the gnuplot
        @param gpFile: name of the gnuplot file
        @param inFile: the list of files to be used as input
        '''        
        self.log.debug("Writing gnuplot file for: " + name)
        fh = open(gpFile, "w")
        self.writeMainContent(fh,self.det_header)
        fh.write("set title \"Per process CPU utilization\" \n");
        if format == "pdf":
            fh.write("set output \"| ps2pdf - " + outFile + "\"\n")
        else:
            fh.write("set output \"" + outFile + "\"\n")
        fh.write("\n")
        fh.write("plot \"" + inFile + "\" using 1:3 title \"" + self.getDescriptionForName(name) + " usr\" with lines, \\\n")
        fh.write(" \"" + inFile +  "\" using 1:4 title \"" + self.getDescriptionForName(name) + " sys\" with lines, \\\n")
        fh.write(" \"" + inFile +  "\" using 1:6 title \"" + self.getDescriptionForName(name) + " total\" with lines \n")
        fh.close()
        self.executeGnuPlot(gpFile)

class pidPerProcessMemGenerator(pidstatGnuplotGenerator):
    '''
    Generator for the pidstat per process memory gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''    
    def __init__(self,format):
        pidstatGnuplotGenerator.__init__(self,format)

    def writeGnuPlotFile(self,name,outFile, gpFile, inFile):
        '''
        Generate the gnuplot configuration file and execute gnuplot for that file
        
        @param name: the name of series
        @param outFile: name of the file storing the chart, result of the gnuplot
        @param gpFile: name of the gnuplot file
        @param inFile: the list of files to be used as input
        '''
        self.log.debug("Writing gnuplot file for: " + name)
        fh = open(gpFile, "w")
        self.writeMainContent(fh,self.det_header)
        fh.write("set title \"Per process Mem utilization\" \n");
        if format == "pdf":
            fh.write("set output \"| ps2pdf - " + outFile + "\"\n")
        else:
            fh.write("set output \"" + outFile + "\"\n")
        
        fh.write("set ylabel \"Size [kB]\"");
        fh.write("\n")
        fh.write("plot \"" + inFile + "\" using 1:11 title \"" + self.getDescriptionForName(name) + " RSS\" with lines, \\\n")
        fh.write(" \"" + inFile +  "\" using 1:10 title \"" + self.getDescriptionForName(name) + " VSZ\" with lines\n")
        fh.close()
        self.executeGnuPlot(gpFile)
    
class pidstatIoGenerator(pidstatGnuplotGenerator):
    '''
    Generator for the pidstat IO gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''
    map = {
           "15" : " rd/s",
           "16" : " wr/s"
           }

    def __init__(self, dirHandler):
        pidstatGnuplotGenerator.__init__(self)
        self.ylabel = "Units"
        self.dirHandler = dirHandler

    def getDescriptionForName(self,name,column):
        '''
        @return: the description of the series to be displayed on the chart
        '''
        return pidstatGnuplotGenerator.getDescriptionForName(self, name) + self.map[column]

    def writeGnuPlotFile(self, outFile, gpFile, heap_io, filemap):            
        '''
        Generate the gnuplot configuration file and execute gnuplot for that file
        
        @param outFile: name of the file storing the chart, result of the gnuplot
        @param gpFile: name of the gnuplot file
        @param heap_io: the list of the series names for which the description shall be displayed in the legend
        @param filemap: the list of series to be displayed on the chart 
        '''
        fh = open(gpFile, "w")
        self.writeMainContent(fh,self.sum_header)
        fh.write("set title \"IO utilization\" \n");
        if self.format == "pdf":
            fh.write("set output \"| ps2pdf - " + outFile + "\"\n")
        else:
            fh.write("set output \"" + outFile + "\"\n")
        fh.write("\n")
        fh.write("set ylabel \""+self.ylabel+"\" \n")        
        self.writeSeries(fh, self.map.keys(), heap_io, filemap);
        fh.flush()
        fh.close()
        self.executeGnuPlot(gpFile)
        return outFile


class pidPerProcessIoGenerator(pidstatGnuplotGenerator):
    '''
    Generator for the pidstat per process IO gnuplot charts
    
    @author: Krystian Brachmanski    
    @requires: 
    '''    
    def __init__(self,format):
        pidstatGnuplotGenerator.__init__(self,format)

    def writeGnuPlotFile(self,name, outFile, gpFile, inFile):
        '''
        Generate the gnuplot configuration file and execute gnuplot for that file
        
        @param name: the name of series
        @param outFile: name of the file storing the chart, result of the gnuplot
        @param gpFile: name of the gnuplot file
        @param inFile: the list of files to be used as input
        '''
        self.log.debug("Writing gnuplot file for: " + name)
        fh = open(gpFile, "w")
        self.writeMainContent(fh,self.det_header)
        fh.write("set title \"Per process I/O statistics\" \n");
        if format == "pdf":
            fh.write("set output \"| ps2pdf - " + outFile + "\"\n")
        else:
            fh.write("set output \"" + outFile + "\"\n")
        
        fh.write("set ylabel \"Units\" \n");
        fh.write("\n")
        fh.write("plot \"" + inFile + "\" using 1:15 title \"" + self.getDescriptionForName(name) + " kB rd\/s\" with lines, \\\n")
        fh.write(" \"" + inFile + "\" using 1:16 title \"" + self.getDescriptionForName(name) + " kB wr\/s\" with lines\n")
        fh.close()
        self.executeGnuPlot(gpFile)
        return outFile
        
